﻿using System;
using System.Drawing;
using System.Linq;
// ColorPicker, Copyright Mirosław M. Mączka.  All Rights Reserved. This code released under the terms of the Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

namespace ColorPicker
{
    //===============================================================================================================================================
    public class ColorRGB
    {
        public byte R { get; set; }
        public byte G { get; set; }
        public byte B { get; set; }
        //
        public override string ToString()
        {
            return string.Format("{0:X2},{1:X2},{2:X2}", R, G, B);
        }
        public static ColorsCooked ColorRGB2ColorCooked(ColorRGB rgb)
        {
            ColorsCooked c = new ColorsCooked(null, rgb.R, rgb.G, rgb.B);
            // szukamy nazwy koloru o identycznej wartości numerycznej
            ColorNameWithSource[] cnws = (from cc in CColors.ColorsTable where cc.ColorRGB == c.ColorRGB select cc.ColorNames).FirstOrDefault();
            if (null != cnws)
            {
                //jest, więc sprawdźmy, czy nie ma koloru Win dla tej wartosci
                ColorNameWithSource cms1 = (from c1 in cnws where c1.Source == "+" || c1.Source == "W" select c1).FirstOrDefault();
                if (null != cms1) cnws = new ColorNameWithSource[] { cms1 };
            }

            if (null != cnws)
            {
                c.ColorNames = new ColorNameWithSource[] { cnws[0] };
            }
            else
            {
                //koloru o takiej wartsci nie ma
                c.ColorNames = new ColorNameWithSource[] { new ColorNameWithSource() { Name = string.Format("RGB{0:X}", c.ColorRGB), Source = "A" } };
            }
            return c;
        }
        public static ColorRGB ColorCooked2ColorRGB(ColorsCooked c)
        {
            return new ColorRGB() { R = c.ColorR, G = c.ColorG, B = c.ColorB };
        }
    }
    //===============================================================================================================================================
    public class ColorHSL
    {
        public double Hue { get; set; }
        public double Saturation { get; set; }
        public double Lightness { get; set; }
        //
        public ColorHSL()
        {
            this.Hue = 0.0;
            this.Saturation = 0.0;
            this.Lightness = 0.0;
        }

        public ColorHSL(ColorHSL hsl)
        {
            this.Hue = hsl.Hue;
            this.Saturation = hsl.Saturation;
            this.Lightness = hsl.Lightness;
        }
        //
        public override string ToString()
        {
            return string.Format("{0}°,{1},{2}", Math.Round(360.0 * Hue, 1), (int)(255 * Saturation), (int)(255 * Lightness));
        }
    }
    //===============================================================================================================================================


    public class CColorManipulations
    {
        //http://serennu.com/colour/colourcalculator.php
        //http://easyrgb.com/index.php?X=MATH&H=19#text19
        //http://serennu.com/colour/rgbtohsl.php
        //http://www.sessions.edu/color-calculator
        //
        //
        //http://www.cattail.nu/newton/index.html
        //
        //
        public static double NormalizeAngle(double d)
        {
            d %= 360.0;
            if (d < 0.0)
            {
                return 360.0 + d;
            }
            else
            {
                return d;
            }
        }
        //
        //===========================================================================================================
        //
        public static ColorHSL RgbToHsl(ColorRGB rgb)
        {
            ColorHSL hsl = new ColorHSL();
            System.Drawing.Color c = Color.FromArgb(rgb.R, rgb.G, rgb.B);
            hsl.Hue = c.GetHue();
            hsl.Lightness = c.GetBrightness();
            hsl.Saturation = c.GetSaturation();
            //
            return hsl;
        }
        //
        //===========================================================================================================
        //http://polymathprogrammer.com/2012/05/30/converting-hsl-to-rgb/
        //public static ColorRGB RgbFromHsl(double Hue, double Saturation, double Lightness)
        public static ColorRGB HslToRgb(ColorHSL hsl)
        {
            double fChroma = (1.0 - Math.Abs(2.0 * hsl.Lightness - 1.0)) * hsl.Saturation;
            double fHue = hsl.Hue / 60.0;
            double fHueMod2 = fHue;
            while (fHueMod2 >= 2.0) fHueMod2 -= 2.0;
            double fTemp = fChroma * (1.0 - Math.Abs(fHueMod2 - 1.0));

            double fRed = 0, fGreen = 0, fBlue = 0;
            if (fHue < 1.0)
            {
                fRed = fChroma;
                fGreen = fTemp;
                fBlue = 0;
            }
            else if (fHue < 2.0)
            {
                fRed = fTemp;
                fGreen = fChroma;
                fBlue = 0;
            }
            else if (fHue < 3.0)
            {
                fRed = 0;
                fGreen = fChroma;
                fBlue = fTemp;
            }
            else if (fHue < 4.0)
            {
                fRed = 0;
                fGreen = fTemp;
                fBlue = fChroma;
            }
            else if (fHue < 5.0)
            {
                fRed = fTemp;
                fGreen = 0;
                fBlue = fChroma;
            }
            else if (fHue < 6.0)
            {
                fRed = fChroma;
                fGreen = 0;
                fBlue = fTemp;
            }
            else
            {
                fRed = 0;
                fGreen = 0;
                fBlue = 0;
            }

            double fMin = hsl.Lightness - 0.5 * fChroma;
            fRed += fMin;
            fGreen += fMin;
            fBlue += fMin;

            fRed *= 255.0;
            fGreen *= 255.0;
            fBlue *= 255.0;
            //Math.Min(Math.Max(0, (int)Math.Round(fRed)), 255);
            return new ColorRGB() { R = (byte)Math.Min(Math.Max(0, (int)Math.Round(fRed)), 255), G = (byte)Math.Min(Math.Max(0, (int)Math.Round(fGreen)), 255), B = (byte)Math.Min(Math.Max(0, (int)Math.Round(fBlue)), 255) };
        }
        /// <summary>
        /// This is the color opposite on the color wheel.
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static ColorRGB GetComplementColor(ColorRGB rgb)
        {
            ColorHSL hsl = RgbToHsl(rgb);
            //
            hsl.Hue = NormalizeAngle(hsl.Hue + 180.0);
            //
            return HslToRgb(hsl);
        }
        //
        /// <summary>
        /// Colors from the same family on the wheel. This will include lighter, darker and differently saturated versions of the color. 
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static ColorRGB GetMonochromaticColor(ColorRGB rgb)
        {
            ColorHSL hsl = RgbToHsl(rgb);
            //
            hsl.Saturation *= (2.0 / 3.0);
            //
            return HslToRgb(hsl);
        }

        /// <summary>
        /// Colors that are equally spaced from each other on the color wheel. (-/+ 120 deg)
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static ColorRGB[] GetTriadicColors(ColorRGB rgb)
        {
            ColorHSL hsl = RgbToHsl(rgb);
            //
            ColorHSL[] hsls = new ColorHSL[] { new ColorHSL(hsl), new ColorHSL(hsl) };
            ColorRGB[] rgbs = new ColorRGB[2];
            //
            hsls[0].Hue = NormalizeAngle(hsls[0].Hue - 120.0); rgbs[0] = HslToRgb(hsls[0]);
            hsls[1].Hue = NormalizeAngle(hsls[1].Hue + 120.0); rgbs[1] = HslToRgb(hsls[1]);
            //
            return rgbs;
        }


        /// <summary>
        /// Colors of the same color temperature near each other on the wheel. (-/+ 30 deg)
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static ColorRGB[] GetAnalogousColors(ColorRGB rgb)
        {
            ColorHSL hsl = RgbToHsl(rgb);
            //
            ColorHSL[] hsls = new ColorHSL[] { new ColorHSL(hsl), new ColorHSL(hsl) };
            ColorRGB[] rgbs = new ColorRGB[2];
            //
            hsls[0].Hue = NormalizeAngle(hsls[0].Hue - 30.0); rgbs[0] = HslToRgb(hsls[0]);
            hsls[1].Hue = NormalizeAngle(hsls[1].Hue + 30.0); rgbs[1] = HslToRgb(hsls[1]);
            //
            return rgbs;
        }

        /// <summary>
        /// This color scheme combines the two colors on either side of a color’s complement. (-/+ 150 deg)
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static ColorRGB[] GetSplitComplementsColors(ColorRGB rgb)
        {
            ColorHSL hsl = RgbToHsl(rgb);
            //
            ColorHSL[] hsls = new ColorHSL[] { new ColorHSL(hsl), new ColorHSL(hsl) };
            ColorRGB[] rgbs = new ColorRGB[2];
            //
            hsls[0].Hue = NormalizeAngle(hsls[0].Hue - 150.0); rgbs[0] = HslToRgb(hsls[0]);
            hsls[1].Hue = NormalizeAngle(hsls[1].Hue + 150.0); rgbs[1] = HslToRgb(hsls[1]);
            //
            return rgbs;
        }
        //===============================================================================================================================================
        //===============================================================================================================================================
#if false
    const int HLSMAX  = 0;//RANGE; /* H,L, and S vary over 0-HLSMAX */ 
   const int RGBMAX =  255;   /* R,G, and B vary over 0-RGBMAX */ 
                           /* HLSMAX BEST IF DIVISIBLE BY 6 */ 
                           /* RGBMAX, HLSMAX must each fit in a byte. */ 

   /* Hue is undefined if Saturation is 0 (grey-scale) */ 
   /* This value determines where the Hue scrollbar is */ 
   /* initially set for achromatic colors */ 
   const int UNDEFINED = (HLSMAX*2/3);

   void  RGBtoHLS(byte R, byte G, byte B)
   {
      //WORD R,G,B;          /* input RGB values */ 
      byte cMax,cMin;      /* max and min RGB values */ 
   Int16  Rdelta,Gdelta,Bdelta; /* intermediate value: % of spread from max*/
            float H;//0-360
            float L;//0-1
            float S;//0-1

 
      /* get R, G, and B out of DWORD */
   /* calculate lightness */ 
      cMax = System.Math.Max( System.Math.Max(R,G), B);
      cMin = System.Math.Min( System.Math.Min(R,G), B);
      L = ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX);

      if (cMax == cMin) {           /* r=g=b --> achromatic case */ 
         S = 0;                     /* saturation */ 
         H = UNDEFINED;             /* hue */ 
      }
      else {                        /* chromatic case */ 
         /* saturation */ 
         if (L <= (HLSMAX/2))S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
         else S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) ) / (2*RGBMAX-cMax-cMin);

         /* hue */ 
      Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
      Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
      Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);

         if (R == cMax)H = Bdelta - Gdelta;
         else if (G == cMax)H = (HLSMAX/3) + Rdelta - Bdelta;
         else /* B == cMax */ H = ((2*HLSMAX)/3) + Gdelta - Rdelta;

         if (H < 0)H += HLSMAX;
         if (H > HLSMAX)H -= HLSMAX;
      }
   }
#endif
        //===============================================================================================================================================
#if false
   /* utility routine for HLStoRGB */ 
   WORD HueToRGB(n1,n2,hue)
   WORD n1;
   WORD n2;
   WORD hue;
   {
      /* range check: note values passed add/subtract thirds of range */ 
      if (hue < 0)
         hue += HLSMAX;
 
      if (hue > HLSMAX)
         hue -= HLSMAX;

      /* return r,g, or b value from this tridrant */ 
      if (hue < (HLSMAX/6))
          return ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) );
      if (hue < (HLSMAX/2))
         return ( n2 );
      if (hue < ((HLSMAX*2)/3))
         return ( n1 +    (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6))
   );
      else
         return ( n1 );
   }
        //===============================================================================================================================================
   DWORD HLStoRGB(hue,lum,sat)
   WORD hue;
   WORD lum;
   WORD sat;
    {
       WORD R,G,B;                /* RGB component values */ 
      WORD  Magic1,Magic2;       /* calculated magic numbers (really!) */ 

      if (sat == 0) {            /* achromatic case */ 
         R=G=B=(lum*RGBMAX)/HLSMAX;
         if (hue != UNDEFINED) {
            /* ERROR */ 
          }
       }
      else  {                    /* chromatic case */ 
         /* set up magic numbers */ 
         if (lum <= (HLSMAX/2))
            Magic2 = (lum*(HLSMAX + sat) + (HLSMAX/2))/HLSMAX;
         else
            Magic2 = lum + sat - ((lum*sat) + (HLSMAX/2))/HLSMAX;
         Magic1 = 2*lum-Magic2;

         /* get RGB, change units from HLSMAX to RGBMAX */ 
         R = (HueToRGB(Magic1,Magic2,hue+(HLSMAX/3))*RGBMAX +
   (HLSMAX/2))/HLSMAX;
         G = (HueToRGB(Magic1,Magic2,hue)*RGBMAX + (HLSMAX/2)) / HLSMAX;
         B = (HueToRGB(Magic1,Magic2,hue-(HLSMAX/3))*RGBMAX +
   (HLSMAX/2))/HLSMAX;
      }
      return(RGB(R,G,B));
    }
#endif
        //===============================================================================================================================================
    }//class
}//namespace
